﻿using System;
using System.ComponentModel;
using System.Configuration;
using System.IO;
using System.Net;
using NLog;

namespace TeamCityConfigurationMonitor.Core
{
    public class FileConfiguration : IConfiguration
    {
        private string username;
        private string password;
        private readonly Logger logger = LogManager.GetCurrentClassLogger();
        private string defaultWorkspacePath;
        private const string DefaultWorkspaceName = "TeamCityConfigurationMonitor";

        public FileConfiguration()
        {
            LoadConfiguration();
            LogConfiguration();
        }

        private void LoadConfiguration()
        {
            defaultWorkspacePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), WorkspaceName);
            ConfigurationLocation =  GetConfigurationLocation();
            TfsUri =  GetMandatorySetting<Uri>(Settings.TfsUrl);
            ServerPath = GetServerPath();
            ChangeNotificationTimeoutInSeconds = GetOptionalSetting(Settings.ChangeNotificationTimeout, 10);
            username = GetOptionalSetting<string>(Settings.Username);
            password = GetOptionalSetting<string>(Settings.Password);
        }

        private void LogConfiguration()
        {
            logger.Info("Configuration loaded");
            logger.Debug("TeamCity configuration directory: {0}", ConfigurationLocation);
            logger.Debug("Tfs server url: {0}", TfsUri);
            logger.Debug("Server path: {0}", ServerPath);
            logger.Debug("Workspace path: {0}", WorkspacePath);
            logger.Debug("Workspace name: {0}", WorkspaceName);
            logger.Debug("Change notification timeout: {0}s", ChangeNotificationTimeoutInSeconds);
            logger.Debug("Files pattern: {0}", FilesToMonitor);
            logger.Debug("Username: {0}", username ?? "no username");
            logger.Debug("Password: {0}", password != null ? new string('*', password.Length) : "no password");
        }

        private static string GetConfigurationLocation()
        {
            var dataFolder = GetMandatorySetting<string>(Settings.TeamCityDataFolder);

            if (!Path.IsPathRooted(dataFolder))
                throw new ConfigurationErrorsException(string.Format("{0} setting should be an absolute path", Settings.TeamCityDataFolder));

            var config = Path.Combine(dataFolder, "config");

            if (!Directory.Exists(config) || FileSystem.IsEmpty(config))
                throw new InvalidOperationException("TeamCity configuration directory does not exist, is empty or is not accessible");

            return config;
        }

        private static bool TryGetSetting(string settingKey, out string value)
        {
            value = ConfigurationManager.AppSettings[settingKey];
            return !string.IsNullOrEmpty(value);
        }

        private static T GetMandatorySetting<T>(string settingKey)
        {
            string value;

            if (TryGetSetting(settingKey, out value))
                return Convert<T>(value);

            throw new ConfigurationErrorsException(string.Format("{0} setting is mandatory", settingKey));
        }

        private static T GetOptionalSetting<T>(string settingKey, T @default = default(T))
        {
            string value;

            return TryGetSetting(settingKey, out value) ? Convert<T>(value) : @default;
        }

        private static T Convert<T>(string value)
        {
            var typeConverter = TypeDescriptor.GetConverter(typeof(T));

            if (typeConverter != null)
                return (T)typeConverter.ConvertFromString(value);

            return (T)System.Convert.ChangeType(value, typeof(T));
        }

        private static string GetServerPath()
        {
            var path = GetMandatorySetting<string>(Settings.ServerPath);

            if (!path.StartsWith("$/"))
                throw new InvalidOperationException(string.Format("{0} must start with $/", Settings.ServerPath));

            return path;
        }

        public Uri TfsUri { get; private set; }

        public string ConfigurationLocation { get; private set; }

        public int ChangeNotificationTimeoutInSeconds { get; private set; }

        public string WorkspacePath
        {
            get
            {
                
                return GetOptionalSetting(Settings.WorkspacePath, defaultWorkspacePath);
            }
        }

        public string ServerPath { get; private set; }

        public string WorkspaceName
        {
            get { return GetOptionalSetting(Settings.WorkspaceName, DefaultWorkspaceName); }
        }

        public bool HasCredentials
        {
            get { return !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password); }
        }

        public ICredentials Credentials
        {
            get
            {
                if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
                    throw new ConfigurationErrorsException(
                        string.Format("If you want to authenticate with supplied credentials you need to put both {0} and {1} settings in the configuration file",
                                      Settings.Username, Settings.Password));

                var split = username.Split(new[] { @"\" }, StringSplitOptions.RemoveEmptyEntries);

                return split.Length > 1
                           ? new NetworkCredential(split[1], password, split[0])
                           : new NetworkCredential(username, password);
            }
        }

        public string FilesToMonitor
        {
            get { return "*.*"; }
        }
    }
}